/**
 * \class Server
 *
 * \note Debugging on the Server class was done by TW Foster
 *
 * \author TW Foster
 *
 * \version $Revision: 2.3 $
 *
 * \date $Date: 2014/09/24 21:25:06 $
 *
 * Created on: Thurs Sep 24 14:15:56 2014
 *
 */

#include <QtCore>
#include "Server.h"
#include "ClientHandle.h"
#include <QNetworkInterface>
#include "gamemaster.h"

Server::Server(QObject *parent, GameMaster *_gm) : QTcpServer(parent)
{
    gm = _gm;

    num_clients = 0;

    directions = new QStringList();

    functionFlag = "";

    if (!this->listen(QHostAddress::Any,5555)) {
        qDebug() << (tr("S|Unable to start the server: %1.").arg(this->errorString()));
        close();
        return;
    } else {
        qDebug() << (tr("S|Server listening on port 5555."));
        qDebug() << serverAddress().toString();
    }
}

QString Server::getIP()
{
    QString temp = "";
    int i = 0;
    foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) {
        if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress(QHostAddress::LocalHost))
        {
            i++;
            if(i == 1)
                temp = address.toString();
        }
    }
    return temp;
}

Server::~Server()
{
}

void Server::incomingConnection(qintptr socketDescriptor)
{
    qDebug() << ("S|Incoming client connection detected...");

    ClientThread *client = new ClientThread(socketDescriptor);
    connect(client, SIGNAL(receivedMessage(QString)), this, SLOT(processMsg(QString)));
    connect(client, SIGNAL(clientClosed(int)), this, SLOT(threadFinished(int)), Qt::DirectConnection);
    connect(client, SIGNAL(finished()), client, SLOT(deleteLater()));

//    if( !directions->contains("North"))
//    {
//        client->setDirection(0);
//        directions->append("North");
//    }
//    else if( !directions->contains("East"))
//    {
//        client->setDirection(1);
//        directions->append("East");
//    }
//    else if( !directions->contains("South"))
//    {
//        client->setDirection(2);
//        directions->append("South");
//    }
//    else if(!directions->contains("West"))
//    {
//        client->setDirection(3);
//        directions->append("West");
//    }

    clientList.append(client);

    client->start();
}

void Server::processMsg(const QString &msg)
{
    ClientThread *clSender = (ClientThread*)sender();
    ClientHandle *client = clSender->getClient();

    QString str = msg;
    str = str.replace("{", "|");
    str = str.replace("}", "|");
    //qDebug() << str;

    QStringList split = str.split("|");
    QStringList reals;

    for (int i = 0; i < split.size(); ++i)
        if( split.at(i).length() > 4)
            reals.append(QString(tr("{%1}").arg(split.at(i))));

    QList<QJsonObject> json_list;
    for( int i=0; i<reals.size(); ++i)
    {
        QJsonDocument jdoc = QJsonDocument::fromJson(reals.at(i).toUtf8());
        QJsonObject jobj = jdoc.object();
        json_list.append(jobj);
    }

    for( int i=0; i<json_list.size(); ++i)
    {
        QString type;
        QJsonObject obj = json_list.at(i);

        type = obj["Type"].toString().toUpper();
        qDebug() << QString(tr("S|Message received: %1").arg(type));

        if( type == "JOIN")
        {
            if( num_clients < 3)
            {
                //num_clients++;
                client->setName(obj["Screen Name"].toString());

                qDebug() << client->getName();

                QJsonObject json;
                json.insert("Type", QJsonValue("Player List"));

                if( clientList.size() == 1) {
                    json.insert("North", QJsonValue(clientList.at(0)->getClient()->getName()));
                    json.insert("South", QJsonValue(""));
                    json.insert("East", QJsonValue(""));
                    json.insert("West", QJsonValue(""));
                } else if( clientList.size() == 2) {
                    json.insert("North", QJsonValue(clientList.at(0)->getClient()->getName()));
                    json.insert("South", QJsonValue(clientList.at(1)->getClient()->getName()));
                    json.insert("East", QJsonValue(""));
                    json.insert("West", QJsonValue(""));
                } else if( clientList.size() == 3) {
                    json.insert("North", QJsonValue(clientList.at(0)->getClient()->getName()));
                    json.insert("South", QJsonValue(clientList.at(1)->getClient()->getName()));
                    json.insert("East", QJsonValue(clientList.at(2)->getClient()->getName()));
                    json.insert("West", QJsonValue(""));
                } else if( clientList.size() == 4) {
                    json.insert("North", QJsonValue(clientList.at(0)->getClient()->getName()));
                    json.insert("South", QJsonValue(clientList.at(1)->getClient()->getName()));
                    json.insert("East", QJsonValue(clientList.at(2)->getClient()->getName()));
                    json.insert("West", QJsonValue(clientList.at(3)->getClient()->getName()));
                }

                functionFlag = "PLAYER LIST";
                multicastMsg(json);

                if( num_clients == 3)
                {
                    // Server is full. Fourth player = our AI
                }
            }
            else
            {
                //sendAcknowledge( client, 0, 8);
            }
        }
        else if( type == "NEW SCREEN NAME")
        {
            ClientHandle *c = getClientByName( obj["Screen Name"].toString());
            c->setName("New Screen Name");
            //sendAcknowledge( client, 1, 0);
        }
        else if( type == "REQUEST SLOT CHANGE")
        {

        }
        else if( type == "BID")
        {
            QJsonObject obj = json_list.at(i);
            QStringList list;
            list.append(obj["Bid action"].toString().toUpper());
            list.append(obj["Trick count"].toString().toUpper());
            list.append(obj["Trump suit"].toString().toUpper());

            //gm->sendBid(list);

            QString name = obj["Screen Name"].toString();

            sendAcknowledge(0, name, 1, 0);
        }
        else if( type == "ACKNOWLEDGEMENT")
        {
            if( functionFlag == "PLAYER LIST")
            {
                qDebug() << "Server got a player list ack";
                // Server sent a message with all players name in it
            }
            else if( functionFlag == "PHASE")
            {
               //gm->ack_startDealing();
            }
            else if( functionFlag.toUpper() == "SEND HAND OWN")
            {
                //gm->ack_sendHand();
            }
            else if( functionFlag.toUpper() == "SEND HAND DUMMY")
            {
                //gm->ack_startSendDummyHand();
            }
            else if( functionFlag.toUpper() == "DEALING")
            {
                //gm->ack_startDealing();
            }
            else if( functionFlag.toUpper() == "REDEALING")
            {
                //gm->ack_startRedealing();
            }
            else if( functionFlag.toUpper() == "BIDDING")
            {
                //gm->ack_startBidding();
            }
            else if( functionFlag.toUpper() == "PLAY")
            {
                //gm->ack_startGame();
            }
            else if( functionFlag == "REQUEST BID")
            {
                //gm->ack_sendBid();
            }
            else if( functionFlag == "CONTRACT")
            {
                //gm->ack_sendContract();
            }
            else if( functionFlag == "DUMMY ID")
            {
                //gm->ack_startSendDummyID();
            }
        }
        else if( type == "PLAY CARD")
        {

        }
        else if( type == "CLIENT QUITS")
        {

        }
        else if( type == "CLIENT ERROR")
        {

        }
        else if( type == "BID UPDATE")
        {
            //gm->ack_sendBid();
        }
        else
        {

        }
    }

    //qDebug() << tr("S|Received message from client [%1.]").arg(clSender->getClient()->peerAddress().toString());

//    if (protocol.getHeader() == Protocol::CLIENT_INFO)
//    {
//        ClientHandle *client = clSender->getClient();
//        client->setName(protocol.getOtherCommands().value("name").toString());

//        //Notify the clients that new one is connected and then the Server sends the client list to the new client
//        Protocol newUser;
//        newUser.setHeader(Protocol::NEW_CLIENT);
//        newUser.setBody("New user has enter!");
//        newUser.setOtherCommands("user", protocol.getOtherCommands().value("name").toString());
//        Protocol clientsList;
//        clientsList.setHeader(Protocol::CLIENTS_LIST);
//        clientsList.setBody("List of connected clients");
//        for (int i = 0; i < clientList.size(); i++)
//        {
//            if (clientList.at(i)->getClient()->getId() != client->getId())
//            {
//                clientList.at(i)->sendMessage(newUser.getFullMessage().toLatin1());
//                clientsList.setOtherCommands("user"+QVariant(clientList.at(i)->getClient()->getId()).toString(),clientList.at(i)->getClient()->getName());
//            }
//        }
//        clSender->sendMessage(clientsList.getFullMessage().toLatin1());
//        //client->waitForBytesWritten();
//    }
//    else if (protocol.getHeader() == Protocol::MULTICAST)
//        multicastMsg(clSender->getClient(), protocol);
//    else if (protocol.getHeader() == Protocol::MESSAGE)
//        clientToClientMsg(protocol);
}

//void Server::clientToClientMsg(const Protocol &p)
//{
//    for (int i = 0; i < clientList.size(); i++)
//    {
//        if (clientList.at(i)->getClient()->getName() == p.getCommandValue("receiver") and p.getBody() != "")
//        {
//            clientList.at(i)->sendMessage(p.getFullMessage().toLatin1());
//            //clientList.at(i)->waitForBytesWritten();
//            break;
//        }
//    }
//}

void Server::threadFinished(int threadId)
{
    QString userExited;
    for (int i = 0; i < clientList.size(); i++)
    {
        if (clientList.at(i)->getThreadId() == threadId)
        {
            userExited = clientList.at(i)->getClient()->getName();
            clientList.removeAt(i);
        }
    }
    //Message to the other clients
//    Protocol *newUser = new Protocol();
//    newUser->setHeader(Protocol::CLIENT_EXIT);
//    newUser->setBody("User has left!");
//    newUser->setOtherCommands("user", userExited);
//    for (int i = 0; i < clientList.size(); i++)
//    clientList.at(i)->sendMessage(newUser->getFullMessage().toLatin1());
//    delete newUser;
    //QLog_Trace("Server", "Thread finished");
}

void Server::multicastMsg(QJsonObject json)
{
    QJsonDocument doc(json);
    QString req = QString::fromUtf8(doc.toJson());

    qDebug() << req;

    for (int i = 0; i < clientList.size(); i++)
        clientList.at(i)->sendMessage(req.toLatin1());
}

QList<ClientHandle*> Server::getClients() const
{
    QList<ClientHandle*> clients;
    for (int i = 0; i < clientList.size(); i++)
        clients.append(clientList.at(i)->getClient());
    return clients;
}

ClientHandle * Server::getClient(int _id) const
{
    for (int i = 0; i < clientList.size(); i++)
        if (clientList.at(i) and clientList.at(i)->getClient()->getId() == _id)
            return clientList.at(i)->getClient();
    return NULL;
}

ClientHandle * Server::getClientByName(const QString &_name) const
{
    for (int i = 0; i < clientList.size(); i++)
        if (clientList.at(i) and clientList.at(i)->getClient()->getName() == _name)
            return clientList.at(i)->getClient();
    return NULL;
}

ClientHandle *Server::getClientByDirection(int dir) const
{
    for (int i = 0; i < clientList.size(); i++)
        if (clientList.at(i) and clientList.at(i)->getClient()->getDirection() == dir)
            return clientList.at(i)->getClient();
    return NULL;
}

ClientThread * Server::getClientThreadByName(const QString &_name)
{
    for (int i = 0; i < clientList.size(); i++)
        if (clientList.at(i) and clientList.at(i)->getClient()->getName() == _name)
            return clientList.at(i);
    return NULL;
}

void Server::writeToClients(const QString &_msg)
{
    for (int i = 0; i < clientList.size(); i++)
        clientList.at(i)->sendMessage(_msg.toLatin1());
}

void Server::writeJSON(QJsonObject json, ClientThread *client)
{
    QJsonDocument doc(json);
    QString req = QString::fromUtf8(doc.toJson());
    //std::string utf = req.toUtf8().constData();
    //qDebug() << req;
    client->sendMessage(req.toLatin1());
}

void Server::getBid( int, QString user_name)
{
    ClientThread *client = getClientThreadByName(user_name);

    QJsonObject json;
    json.insert("Type", QJsonValue("Request"));
    json.insert("Note", QJsonValue("Bid"));

    functionFlag = "REQUEST BID";

    writeJSON(json, client);
}

void Server::sendBid( int, QString client_to, QString client_name, QString bid_action, int trick_count, QString trump_suit)
{
    // Refer to bid update in comms protocol
    // Sends the latest bid from client_name to _client_dir
    ClientThread *client = getClientThreadByName(client_to);

    QJsonObject json;
    json.insert("Type", QJsonValue("Bid Update"));
    json.insert("Screen Name", QJsonValue(client_name));
    json.insert("Bid Action", QJsonValue(bid_action));
    json.insert("Trick Count", QJsonValue(trick_count));
    json.insert("Trump Suit", QJsonValue(trump_suit));

    functionFlag = "BID UPDATE";

    writeJSON(json, client);
}

void Server::getMove( int, QString name)
{
    ClientThread *client = getClientThreadByName(name);

    QJsonObject json;
    json.insert("Type", QJsonValue("Request"));
    json.insert("Note", QJsonValue("Play"));

    writeJSON(json, client);
}

void Server::getDummyMove( int, QString name)
{
    ClientThread *client = getClientThreadByName(name);

    QJsonObject json;
    json.insert("Type", QJsonValue("Request"));
    json.insert("Note", QJsonValue("Dummy Play"));

    writeJSON(json, client);
}

void Server::sendMove( QString client_name, int, QString suit, QString value)
{
    ClientThread *client = getClientThreadByName(client_name);

    QJsonObject json;
    json.insert("Type", QJsonValue("Play Update"));
    json.insert("Screen Name", QJsonValue("Screen Name"));
    json.insert("Suit", QJsonValue(suit));
    json.insert("Value", QJsonValue(value));

    writeJSON(json, client);
}

void Server::sendAcknowledge(int, QString name, int success, int error_code)
{
    ClientThread *client = getClientThreadByName(name);

    QJsonObject json;
    json.insert("Type", QJsonValue("Acknowledgement"));
    json.insert("Note", QJsonValue(success));
    json.insert("Error code", QJsonValue(error_code));

    writeJSON(json, client);
}

void Server::sendPhase(int, QString user_name, QString phase)
{

    ClientThread *client = getClientThreadByName(user_name);

    QJsonObject json;
    json.insert("Type", QJsonValue("Phase"));
    json.insert("Note", QJsonValue(phase));

    functionFlag = phase;

    writeJSON(json, client);
}

void Server::sendContract(QString client_name, int trick_count, QString trump_suit)
{
    ClientThread *client = getClientThreadByName(client_name);

    QJsonObject json;
    json.insert("Type", QJsonValue("Contract"));
    json.insert("Trick Count", QJsonValue(trick_count));
    json.insert("Trump Suit", QJsonValue(trump_suit));

    functionFlag = "CONTRACT";

    writeJSON(json, client);
}

void Server::sendWonUpdate( int, QString client_to, QString team, QString note)
{
    ClientThread *client = getClientThreadByName(client_to);

    QJsonObject json;
    json.insert("Type", QJsonValue("Won update"));
    json.insert("Winning team", QJsonValue(team));
    json.insert("Note", QJsonValue(note));

    writeJSON(json, client);
}

void Server::sendDummyID( int, QString client_name, QString dummy_name)
{
    ClientThread *client = getClientThreadByName(client_name);

    QJsonObject json;
    json.insert("Type", QJsonValue("Dummy Identification"));
    json.insert("Dummy", QJsonValue(dummy_name));

    functionFlag = "DUMMY ID";

    writeJSON(json, client);
}

void Server::sendCardArray( int, QString user_name, QString cards, QString note)
{
    ClientThread *client = getClientThreadByName(user_name);

    QJsonObject json;
    json.insert("Type", QJsonValue("Receive cards"));
    json.insert("Card array", QJsonValue(cards));
    json.insert("Note", QJsonValue(note));

    functionFlag = "SEND HAND " + note;

    writeJSON(json, client);
}

void Server::scoreUpdate( int, QString user_name, QString wabove, QString wbelow, QString tabove, QString tbelow)
{
    ClientThread *client = getClientThreadByName(user_name);

    QJsonObject json;
    json.insert("Type", QJsonValue("Score Update"));
    json.insert("We Above", QJsonValue(wabove));
    json.insert("We Below", QJsonValue(wbelow));
    json.insert("They Above", QJsonValue(tabove));
    json.insert("They Below", QJsonValue(tbelow));

    writeJSON(json, client);
}
